#ifndef _GAME_SERVICE_H_
#define _GAME_SERVICE_H_

#include "BaseService.h"
#include "Timer.h"
#include "GameConfigManager.h"
#include "LogEx.h"
#include "EventWorkerEx.h"
#include "IoServiceEx.h"
#include "SetupSocket.h"
#ifdef _USE_CURL_
#include "CurlManager.h"
#endif
#include "Hall.h"
#include "IPacketParser.h"

// not use any more.
// using ServerManager.

#define GAME_LOG_INIT() \
	LOG_INIT(\
		CGameConfigManager::Instance().GetLogConfig().file.c_str(),\
		CGameConfigManager::Instance().GetLogConfig().rollingMode,\
		CGameConfigManager::Instance().GetLogConfig().minLevel,\
		CGameConfigManager::Instance().GetLogConfig().maxLevel );

class CGameServiceManager;
class IGameService : public SGLib::IEventEx
{
public:
	virtual ~IGameService(){}
	virtual bool Start() = 0;
	virtual void Stop() = 0;
	virtual void Connect(const char *ip, s32 port) = 0;
	virtual void ConnectAllServers() = 0;
    virtual s32 AddTimer(u32 elapse, SGLib::CallBackFunc func, void *data, s32 len, bool isPersist) = 0;
    virtual void DelTimer(s32 nTimerId) = 0;
	virtual SGLib::IPacketParser* GetPacketParser() = 0;
	virtual SGLib::CIoServiceExPool& GetIoServicePool() = 0;
		
	virtual void HandleEvent(s32 paramLen, char *paramData){}
};

template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount>
class CGameService : public IGameService
{
public:
	struct CallBackHead
	{
		SGLib::CallBackFunc func;
		s32 len;
		void *obj;
	};
	enum
	{
		E_Timer_DataLen = SGLib::CEventWorkerEx::E_Max_SessionEvent_ParamSize - sizeof(CallBackHead) - sizeof(void*),
	};
	struct CallBackData
	{
		CallBackHead head;
		char data[E_Timer_DataLen];
	};

public:
	// 固定使用单线程模式
    CGameService() : 
		m_port(CGameConfigManager::Instance().GetServerConfig().port),
		m_timer(),
		m_service(
			m_parser, 
			CGameConfigManager::Instance().GetServerConfig().connCount, 
			CGameConfigManager::Instance().GetServerConfig().szSend,
			CGameConfigManager::Instance().GetServerConfig().szRecv,
			CGameConfigManager::Instance().GetServerConfig().rpcConnCount,
			CGameConfigManager::Instance().GetServerConfig().rpcSzSend,
			CGameConfigManager::Instance().GetServerConfig().rpcSzRecv,
			1, true, 1,
			CGameConfigManager::Instance().GetServerConfig().eventQueueSize)
    {
        SGLib::CSetupSocket::Instance().IgnoreSignal();
    }
	
	virtual ~CGameService()
	{
	}

    virtual bool Start()
    {
		return (m_timer.Start() && m_service.Start( m_port ));
    }

    virtual void Stop()
    {
		m_timer.Stop();
		m_service.Stop();
    }

    virtual void Connect(const char *ip, s32 port)
    {
		m_service.Connect( ip, port, NULL );
    }
	
	virtual void ConnectAllServers()
	{
		s32 count = CGameConfigManager::Instance().GetConnectCount();
		for( s32 i=0; i<count; ++i )
		{
			const ConnectItem *item = CGameConfigManager::Instance().GetConnectItem( i );
			if( item )
			{
				Connect( item->ip.c_str(), item->port );
			}
		}
	}
	
	virtual void HandleEvent(s32 len, char *data)
	{
		// only timer work here
#if 0 
		LOG_DEBUG( "GameService::HandleEvent proc timer event" );
#endif
		SELF_ASSERT( data!=NULL && len>0, return; );
		CallBackData *cbData = (CallBackData*)data;
		cbData->head.func( cbData->data, cbData->head.len );
	}

    virtual s32 AddTimer(u32 elapse, SGLib::CallBackFunc func, void *data, s32 len, bool isPersist)
	{
		if( len > E_Timer_DataLen )
		{
			return SGLib::CTimer<TimerCount>::E_Invalid_TimerId;
		}

		CallBackData cbData;
		cbData.head.obj = &m_service.GetIoServiceExPool();
		cbData.head.func = func;
		cbData.head.len = len;
		if( cbData.head.len > 0 )
		{
			memcpy( cbData.data, data, cbData.head.len );
		}

		return m_timer.AddTimer( elapse, _TimerCallBackFunc, &cbData, sizeof(cbData), isPersist );
	}

    virtual void DelTimer(s32 nTimerId)
	{
		m_timer.DelTimer( nTimerId );
	}

	virtual SGLib::IPacketParser* GetPacketParser()
	{
		return &m_parser;
	}

	virtual SGLib::CIoServiceExPool& GetIoServicePool()
	{
		return m_service.GetIoServiceExPool();
	}

private:
    static void _TimerCallBackFunc(void *data, s32 len);

private:
	s32 m_port;
	TPacketParser m_parser;
	SGLib::CTimer<TimerCount> m_timer;
	CBaseService<TSession, TRpcSession, TPacketParser> m_service;
};

class CGameServiceManager
{
    SINGLETON_CLASS_DEFINE(CGameServiceManager);
public:
	template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount>
	IGameService* CreateGameService()
	{
		m_service = new CGameService<TSession, TRpcSession, TPacketParser, TimerCount>();
		return m_service;
	}

	void DestoryGameService();
	IGameService* GetGameService();
	SGLib::IPacketParser* GetPacketParser();
    s32 AddTimer(u32 elapse, SGLib::CallBackFunc func, void *data, s32 len, bool isPersist=false);
    void DelTimer(s32 nTimerId);

private:
	IGameService *m_service;
};


template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount>
void CGameService<TSession,TRpcSession,TPacketParser,TimerCount>::_TimerCallBackFunc(void *data, s32 len)
{
#if 0
    LOG_DEBUG( "GameService::_TimerCallBackFunc called. try push timer event" );
#endif
    SELF_ASSERT( data!=NULL && len>0, return; );
    CallBackData *cbData = (CallBackData*)data;
    SGLib::CIoServiceExPool *pool = (SGLib::CIoServiceExPool*)(cbData->head.obj);
    SELF_ASSERT( pool, return; );
    // 单线程模式只有一个ioserviceex, 并且eventWorker也只有一个
    SGLib::CIoServiceEx *io = pool->GetIoService();
    IGameService *service = CGameServiceManager::Instance().GetGameService();
    SELF_ASSERT( io && service, return; );
    if( !io->TryPushEvent( 0, len, data, service ) )
    {
        LOG_ERROR( "GameService::_TimerCallBackFunc TryPushEvent failed." );
    }
}

// ===============================================================================================

template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount>
bool StartService(const std::string &cfgfile, bool useCurl)
{
	CGameConfigManager::Instance().Load( cfgfile );
	GAME_LOG_INIT();

#ifdef _USE_CURL_
	if( useCurl )
	{  
		if( !CCurlManager::Instance().Start() )
		{
			LOG_ERROR( "CCurlManager start failed" );
			return false;
		}
	}
#endif
	
	IGameService *service = CGameServiceManager::Instance().CreateGameService<TSession, TRpcSession, TPacketParser, TimerCount>();
	if( service && service->Start() ) 
	{
		service->ConnectAllServers();
		return true;
	}

	return false;
}

template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount, typename TRoom, typename TPlayer>
bool StartServiceEx(const std::string &cfgfile, bool useCurl, s32 roomCount, s32 minRoomPlayerCount, s32 maxRoomPlayerCount, s32 maxRoomBeholderCount)
{
	if( StartService<TSession, TRpcSession, TPacketParser, TimerCount>(cfgfile, useCurl) )
	{
		IGameService *service = CGameServiceManager::Instance().GetGameService();
		SELF_ASSERT( service, return false; );
		return CHall::Instance().Init<TRoom, TPlayer>(
			service, 
			roomCount, 
			minRoomPlayerCount, 
			maxRoomPlayerCount, 
			maxRoomBeholderCount);
	}
	return false;
}

template<s32 none>
void StopService(bool useCurl, bool useHall)
{
#ifdef _USE_CURL_
	if( useCurl )
	{
		CCurlManager::Instance().Stop();
	}
#endif
	IGameService *service = CGameServiceManager::Instance().GetGameService();
	if( service )
	{
		service->Stop();
	}
	CGameServiceManager::Instance().DestoryGameService();
	if( useHall )
	{
		CHall::Instance().Fini();
	}
}

#endif

